<template>
  <div>
    <div class="textBox">{{ text }}</div>
    <audio class="audio" controls loop autoplay>
      <source src="../public/audio/cef.mp3" type="audio/mpeg" />
    </audio>
  </div>
</template>

<script setup>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
import { Water } from "three/examples/jsm/objects/Water2";
import gsap from "gsap";

import { handlerRoll } from "./utils/comUtils";

import { ref, watchEffect } from "vue";

let scenes = ref("");

// 创建场景
const scene = new THREE.Scene();

// 创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.set(-3.23, 8.98, 20);
// camera.position.set(-3.23, 2.98, 4.06);
camera.updateProjectionMatrix();

// 创建渲染器
const renderer = new THREE.WebGLRenderer({
  // 设置抗锯齿
  antialias: true,
  // 设置对数深度缓冲区，优化深度冲突问题
  logarithmicDepthBuffer: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 设置色调映射
// renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
// 允许阴影
renderer.shadowMap.enabled = true;

// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置阻尼
controls.enableDamping = true;
controls.dampingFactor = 0.15;

// 解压模型
const dracoLoader = new DRACOLoader();

dracoLoader.setDecoderPath("./draco/");

// 加载环境纹理
let rgbeLoader = new RGBELoader();
rgbeLoader.load("./textures/sky.hdr", (textures) => {
  // 纹理映射模式为球面
  textures.mapping = THREE.EquirectangularReflectionMapping;
  scene.background = textures;
  scene.environment = textures;
});

// 加载模型
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load("../public/model/scene.glb", (gltf) => {
  let model = gltf.scene;
  // 隐藏模型的水面
  model.traverse((child) => {
    if (child.name === "Plane") child.visible = false;
    if (child.isMesh) {
      child.castShadow = true;
      child.receiveShadow = true;
    }
  });
  scene.add(model);
});

// 创建水面
const waterGeometry = new THREE.CircleGeometry(300, 32);
const water = new Water(waterGeometry, {
  textureWidth: 1024,
  textureHeight: 1024,
  color: 0x00eeff,
  // flowDirection: new THREE.Vector2(1, 1),
  scale: 100,
});
water.rotation.x = -Math.PI / 2;

scene.add(water);

// 添加光源
// const light = new THREE.DirectionalLight(0xffffff, 1);
// scene.add(light);

// 添加点光源组
let radius = 3;
const pointLightGroup = new THREE.Group();
const pointLightArr = [];
for (let i = 0; i < 3; i++) {
  // 创建球当光源
  const sphereGeometry = new THREE.SphereGeometry(0.2, 32, 32);
  const sphereMaterial = new THREE.MeshStandardMaterial({
    color: 0xffffff,
    emissive: 0xffffff,
    emissiveIntensity: 10,
  });
  const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
  sphere.position.set(
    radius * Math.cos((2 * Math.PI * i) / 3),
    Math.cos((2 * Math.PI * i) / 3),
    radius * Math.sin((2 * Math.PI * i) / 3)
  );
  // sphere.castShadow = true;

  pointLightArr.push(sphere);

  const pointLight = new THREE.PointLight(0xffffff, 40, 100);
  // pointLight.castShadow = true;
  pointLight.shadow.bias = -0.003;

  sphere.add(pointLight);
  pointLightGroup.add(sphere);
}

pointLightGroup.position.set(-8, 2.5, -1.5);

scene.add(pointLightGroup);

// 补间动画，是灯泡球动起来
let options = {
  angle: 0,
};
gsap.to(options, {
  angle: Math.PI * 2,
  duration: 10,
  ease: "linear",
  repeat: -1,
  onUpdate: () => {
    pointLightGroup.rotation.y = options.angle;
    pointLightArr.forEach((item, index) => {
      item.position.set(
        radius * Math.cos((2 * Math.PI * index) / 3),
        Math.cos((2 * Math.PI * index) / 3 + options.angle * 5),
        radius * Math.sin((2 * Math.PI * index) / 3)
      );
    });
  },
});

// 添加房间内的灯
const pointLight = new THREE.PointLight(0xffffff, 30, 100);
pointLight.position.set(0.5, 2.3, 0);
// 设置灯光阴影
pointLight.castShadow = true;
// 解决深度冲突问题
pointLight.shadow.bias = -0.01;
scene.add(pointLight);

let starsArr = [];
let endArr = [];

// 实例化漫天星星
const starsInstance = new THREE.InstancedMesh(
  new THREE.SphereGeometry(0.15, 32, 32),
  new THREE.MeshStandardMaterial({
    color: 0xffffff,
    emissive: 0xffffff,
    emissiveIntensity: 10,
  }),
  200
);

for (let index = 0; index < 200; index++) {
  let x = Math.random() * 100 - 50;
  let y = (Math.random() + 2) * 10;
  let z = Math.random() * 100 - 50;
  starsArr.push(new THREE.Vector3(x, y, z));

  let matrix = new THREE.Matrix4();
  matrix.setPosition(x, y, z);
  starsInstance.setMatrixAt(index, matrix);
}

console.log(starsInstance);
scene.add(starsInstance);

// 监听每页文本变化
watchEffect(() => {
  scenes.value = handlerRoll(camera, controls);
  if (scenes.value.type === "stars") {
    makeHeart();
  } else if (scenes.value.type === "init") {
    restoreHeart();
  }
});

// 创建爱心路径
let heartShape = new THREE.Shape();
heartShape.moveTo(25, 25);
heartShape.bezierCurveTo(25, 25, 20, 0, 0, 0);
heartShape.bezierCurveTo(-30, 0, -30, 35, -30, 35);
heartShape.bezierCurveTo(-30, 55, -10, 77, 25, 95);
heartShape.bezierCurveTo(60, 77, 80, 55, 80, 35);
heartShape.bezierCurveTo(80, 35, 80, 0, 50, 0);
heartShape.bezierCurveTo(35, 0, 25, 25, 25, 25);

// 根据爱心获取点
const center = new THREE.Vector3(0, 2, 5);
for (let index = 0; index < 200; index++) {
  let point = heartShape.getPoint(index / 200);
  endArr.push(
    new THREE.Vector3(
      point.x * 0.1 + center.x,
      point.y * 0.1 + center.y,
      center.z
    )
  );
}

// 创建爱心动画
function makeHeart() {
  let params = {
    time: 0,
  };

  gsap.to(params, {
    time: 1,
    duration: 1,
    onUpdate: () => {
      for (let i = 0; i < 200; i++) {
        let x = starsArr[i].x + (endArr[i].x - starsArr[i].x) * params.time;
        let y = starsArr[i].y + (endArr[i].y - starsArr[i].y) * params.time;
        let z = starsArr[i].z + (endArr[i].z - starsArr[i].z) * params.time;
        let matrix = new THREE.Matrix4();
        matrix.setPosition(x, y, z);
        starsInstance.setMatrixAt(i, matrix);
      }
      starsInstance.instanceMatrix.needsUpdate = true;
    },
  });
}

function restoreHeart() {
  let params = {
    time: 0,
  };

  gsap.to(params, {
    time: 1,
    duration: 1,
    onUpdate: () => {
      for (let i = 0; i < 200; i++) {
        let x = endArr[i].x + (starsArr[i].x - endArr[i].x) * params.time;
        let y = endArr[i].y + (starsArr[i].y - endArr[i].y) * params.time;
        let z = endArr[i].z + (starsArr[i].z - endArr[i].z) * params.time;
        let matrix = new THREE.Matrix4();
        matrix.setPosition(x, y, z);
        starsInstance.setMatrixAt(i, matrix);
      }
      starsInstance.instanceMatrix.needsUpdate = true;
    },
  });
}

const render = () => {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
  // console.log(camera.position);
  // console.log(controls.position);
  // console.log(controls.target);
  controls.update();
};

render();
</script>

<style scoped>
* {
  margin: 0;
  padding: 0;
}

canvas {
  width: 100vw;
  height: 100vh;
}

.textBox {
  position: absolute;
  left: 20%;
  top: 20%;
  font-size: 48px;
  color: beige;
  font-weight: bold;
}

.audio {
  position: absolute;
  right: 10%;
  bottom: 10%;
}
</style>
